/*
 * Copyright (c) 2010 Zauber S.A.  -- All rights reserved
 */
package ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.impl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;

import ar.edu.itba.ia2010.geneticos.motor.api.Gen;
import ar.edu.itba.ia2010.geneticos.motor.api.MotorGeneticoContext;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.Expresion;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise.ReplaceExpresion;

/**
 * Clase base para las expresiones 
 * 
 * @author Juan F. Codagnone
 * @since Mar 27, 2010
 */
public abstract class AbstractExpresion implements Expresion {
  
    /** @see Object#toString() */
    @Override
    public final String toString() {
        final StringBuilder sb = new StringBuilder();
        preorder(new VisitorCallback() {
            public Expresion notify(final Expresion e) {
                sb.append(e.getCharacter());
                return null;
            }
        });
        return sb.toString();
    }

    /** @see Expresion#preorder(Expresion.VisitorCallback) */
    public final Expresion preorder(final VisitorCallback c) {
        final Expresion o = c.notify(this);
        if(o == null) {
            for(final ListIterator<Gen<Expresion>> it = listiterator();
                it.hasNext();) {
                final Expresion s = it.next().getValue().preorder(c);
                if(s != null) {
                    it.set(s);
                }
            }
        }
        return o;
    }
    
    /** @see Expresion#preorder(Expresion.VisitorCallback) */
    public final Expresion posorder(final VisitorCallback c) {
        for(final ListIterator<Gen<Expresion>> it = listiterator();
            it.hasNext();) {
            final Expresion e = it.next().getValue();
            final Expresion s = e.posorder(c);
            if(s != null && s != e) {
                it.set(s);
            }
        }
        return c.notify(this);
    }
    
    /** @see Expresion#deepCopy(DeepCopyContext) */
    public final Expresion deepCopy(final DeepCopyContext ctx) {
        if(ctx != null) {
            ctx.i--;
        }
        
        if(ctx != null && ctx.i  == 0 && ctx.subtree == null) {
            ctx.subtree = deepCopy(null);
            return ReplaceExpresion.INSTANCE;
        } else {
            final List<Expresion> clones = new ArrayList<Expresion>(childs.size());
            for(final Expresion c : childs) {
                clones.add(c.deepCopy(ctx));
            }
            return clone(clones);
        }
    }
    
    /** clona el elemento actual */
    protected abstract Expresion clone(final List<Expresion> clones);

    /** @see Gen#getValue() */
    public final Expresion getValue() {
        return this;
    }
    
    /** @see Gen#mutar(Random, double) */
    public final Gen<Expresion> mutar(final Random random, 
            final double probabilidad, final MotorGeneticoContext ctxg)  {
        final ExpresionMotorGeneticoContext ctx = 
            (ExpresionMotorGeneticoContext)ctxg;
        
        Gen<Expresion> ret = null;
        if(random.nextDouble() <= probabilidad) {
            ret = ctx.getGenerator().generate(random, ctx.getMaxDepth());
            
        } else {
            for(final ListIterator<Gen<Expresion>> it = listiterator();
                it.hasNext();) {
                final Gen<Expresion> gen = it.next().mutar(random, probabilidad, 
                        ctx);
                if(gen != null) {
                    it.set(gen);
                }
            }
        }
        return ret;
    }
    
    
    /** @see Object#equals(Object) */
    @Override
    public final boolean equals(final Object obj) {
        boolean ret = false;
        
        if(obj == this) {
            ret = true;
        } else if(obj instanceof AbstractExpresion) {
            final AbstractExpresion e = (AbstractExpresion) obj;
            ret = e.getCharacter() == getCharacter();
            
            if(ret) {
               final Iterator<Expresion> i1 = iterator();
               final Iterator<Expresion> i2 = e.iterator();
               
               while(ret && i1.hasNext() && i2.hasNext()) {
                   ret = i1.next().equals(i2.next());
               }
               if(ret) {
                   ret = i1.hasNext() == i2.hasNext();
               }
           }
        }
        
        return ret;
    }
    
    /** @see Object#hashCode() */
    @Override
    public final int hashCode() {
        int ret = 17;
        
        ret = 37 * ret + getCharacter();
        
        for(final Expresion e : this) {
            ret = 37 * ret + e.hashCode(); 
        }
        
        return ret;
    }
    
    /** childs */
    protected final List<Expresion> childs = new ArrayList<Expresion>(2);

    /** @see Expresion#isTerminal() */
    public final boolean isTerminal() {
        return childs.size() == 0;
    }
    
    /** @see Iterable#iterator() */
    public final Iterator<Expresion> iterator() {
        return new ReadOnlyIterator<Expresion>(childs.iterator());
    }
    
    /** @see Expresion#listiterator() */
    public final ListIterator<Gen<Expresion>> listiterator() {
        final ListIterator<?>  e = childs.listIterator();
        return (ListIterator<Gen<Expresion>>) e;
    }

}
